/*
 * Copyright 2012 Sebastian Annies, Hamburg
 *
 * Licensed under the Apache License, Version 2.0 (the License);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.coremedia.iso;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;

public final class IsoTypeReader {


  public static long readUInt32BE(ByteBuffer bb) {
    long ch1 = readUInt8(bb);
    long ch2 = readUInt8(bb);
    long ch3 = readUInt8(bb);
    long ch4 = readUInt8(bb);
    return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));

  }


  public static long readUInt32(ByteBuffer bb) {
    long i = bb.getInt();
    if (i < 0) {
      i += 1l << 32;
    }
    return i;
  }

  public static int readUInt24(ByteBuffer bb) {
    int result = 0;
    result += readUInt16(bb) << 8;
    result += byte2int(bb.get());
    return result;
  }


  public static int readUInt16(ByteBuffer bb) {
    int result = 0;
    result += byte2int(bb.get()) << 8;
    result += byte2int(bb.get());
    return result;
  }

  public static int readUInt16BE(ByteBuffer bb) {
    int result = 0;
    result += byte2int(bb.get());
    result += byte2int(bb.get()) << 8;
    return result;
  }

  public static int readUInt8(ByteBuffer bb) {
    return byte2int(bb.get());
  }

  public static int byte2int(byte b) {
    return b < 0 ? b + 256 : b;
  }


  /**
   * Reads a zero terminated UTF-8 string.
   *
   * @param byteBuffer the data source
   * @return the string readByte
   * @throws Error in case of an error in the underlying stream
   */
  public static String readString(ByteBuffer byteBuffer) {

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    int read;
    while ((read = byteBuffer.get()) != 0) {
      out.write(read);
    }
    return Utf8.convert(out.toByteArray());
  }

  public static String readString(ByteBuffer byteBuffer, int length) {
    byte[] buffer = new byte[length];
    byteBuffer.get(buffer);
    return Utf8.convert(buffer);

  }

  public static long readUInt64(ByteBuffer byteBuffer) {
    long result = 0;
    // thanks to Erik Nicolas for finding a bug! Cast to long is definitivly needed
    result += readUInt32(byteBuffer) << 32;
    if (result < 0) {
      throw new RuntimeException("I don't know how to deal with UInt64! long is not sufficient and I don't want to use BigInt");
    }
    result += readUInt32(byteBuffer);

    return result;
  }

  public static double readFixedPoint1616(ByteBuffer bb) {
    byte[] bytes = new byte[4];
    bb.get(bytes);

    int result = 0;
    result |= ((bytes[0] << 24) & 0xFF000000);
    result |= ((bytes[1] << 16) & 0xFF0000);
    result |= ((bytes[2] << 8) & 0xFF00);
    result |= ((bytes[3]) & 0xFF);
    return ((double) result) / 65536;

  }

  public static double readFixedPoint0230(ByteBuffer bb) {
    byte[] bytes = new byte[4];
    bb.get(bytes);

    int result = 0;
    result |= ((bytes[0] << 24) & 0xFF000000);
    result |= ((bytes[1] << 16) & 0xFF0000);
    result |= ((bytes[2] << 8) & 0xFF00);
    result |= ((bytes[3]) & 0xFF);
    return ((double) result) / (1 << 30);

  }

  public static float readFixedPoint88(ByteBuffer bb) {
    byte[] bytes = new byte[2];
    bb.get(bytes);
    short result = 0;
    result |= ((bytes[0] << 8) & 0xFF00);
    result |= ((bytes[1]) & 0xFF);
    return ((float) result) / 256;
  }

  public static String readIso639(ByteBuffer bb) {
    int bits = readUInt16(bb);
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < 3; i++) {
      int c = (bits >> (2 - i) * 5) & 0x1f;
      result.append((char) (c + 0x60));
    }
    return result.toString();
  }

  public static String read4cc(ByteBuffer bb) {
    byte[] b = new byte[4];
    bb.get(b);
    return IsoFile.bytesToFourCC(b);
  }

}
